home *** CD-ROM | disk | FTP | other *** search
- (herald computed_goto)
-
- (define (simplify-computed-goto call)
- (simplify (nthcdr (call-proc+args call) ; simplify the default
- (call-exits call)))
- (let ((next (iterate loop ((n (goto-default call)))
- (cond ((not (lambda-node? n)) '#f)
- ((lambda-node? (call-proc (lambda-body n)))
- (loop (call-proc (lambda-body n))))
- (else (lambda-body n)))))
- (var (reference-variable (goto-index call))))
- (cond ((not next) '#f)
- ; ((similar-eq?-call? next var) ; should probably do this as well
- ; (make-goto-call var call next))
- ((similar-goto-call? next var)
- (join-two-gotos call next))
- (else '#f))))
-
- (define (goto-keys call)
- ((call-arg (fx+ 2 (call-exits call))) call))
-
- (define (goto-default call)
- ((call-arg (call-exits call)) call))
-
- (define (goto-index call)
- ((call-arg (fx+ 1 (call-exits call))) call))
-
- (define (join-two-gotos first second)
- (let ((keys1 (literal-value (goto-keys first)))
- (keys2 (literal-value (goto-keys second)))
- (args1 (call-args first))
- (args2 (call-args second))
- (first-default (goto-default first))
- (second-default (goto-default second)))
- (iterate loop ((keys2 keys2) (args2 args2) (args args1) (keys keys1))
- (cond ((null? keys2)
- (let ((new (create-call-node (fx+ 1 (length args))
- (fx- (length args) 2))))
- (relate-call-args new (map detach args))
- (set (call-proc new) (detach (call-proc first)))
- (set (literal-value (goto-keys new)) keys)
- (replace first (detach (lambda-body first-default)))
- (replace first-default (detach second-default))
- (replace second new)
- '#t))
- ((memq? (car keys2) keys1)
- (loop (cdr keys2) (cdr args2) args keys))
- (else
- (loop (cdr keys2) (cdr args2)
- (cons (car args2) args)
- (cons (car keys2) keys)))))))
-
- ; If both values are constants, then replace with the appropriate continuation.
- ; Otherwise check for testing an identifier against a fixnum.
-
- (define (simplify-eq? call)
- (let ((arg1 ((call-arg '4) call))
- (arg2 ((call-arg '5) call)))
- (cond ((and (literal-node? arg1)
- (literal-node? arg2))
- (let ((proc ((call-arg (if (eq? (literal-value arg1)
- (literal-value arg2))
- '1
- '2))
- call))
- (new (create-call-node '1 '1)))
- (detach proc)
- (relate call-proc new proc)
- (replace call new)
- '#t))
- ((and (literal-node? arg1)
- (fixnum? (literal-value arg1))
- (reference-node? arg2))
- (eq?->goto call arg2))
- ((and (literal-node? arg2)
- (fixnum? (literal-value arg2))
- (reference-node? arg1))
- (eq?->goto call arg1))
- (else '#f))))
-
- ; Go down the false branch looking for either a test of the same
- ; identifier or a call to $COMPUTED-GOTO
-
- (define (eq?->goto call ref)
- (simplify (cdr (call-args call)))
- (let ((var (reference-variable ref))
- (next (iterate loop ((n ((call-arg '2) call)))
- (cond ((not (lambda-node? n)) '#f)
- ((lambda-node? (call-proc (lambda-body n)))
- (loop (call-proc (lambda-body n))))
- (else (lambda-body n))))))
- (cond ((not next) '#f)
- ((similar-eq?-call? next var)
- (make-goto-call var call next))
- ((similar-goto-call? next var)
- (add-to-goto next call))
- (else '#f))))
-
- ; Is CALL checking for equality between an identifier and a fixnum?
-
- (define (similar-eq?-call? call var)
- (and (primop-ref? (call-proc call) primop/conditional)
- (eq? 'eq? (primop.id (primop-value ((call-arg '3) call))))
- (or (eq?-arg-check call var '4 '5)
- (eq?-arg-check call var '5 '4))))
-
- (define (eq?-arg-check call var i1 i2)
- (let ((arg1 ((call-arg i1) call))
- (arg2 ((call-arg i2) call)))
- (and (reference-node? arg1)
- (eq? var (reference-variable arg1))
- (literal-node? arg2)
- (fixnum? (literal-value arg2)))))
-
- ; Is CALL a computed goto on the value of VAR?
-
- (define (similar-goto-call? call var)
- (and (id-primop-ref? (call-proc call) 'computed-goto)
- (let ((test ((call-arg (fx+ '1 (call-exits call))) call)))
- (and (reference-node? test)
- (eq? var (reference-variable test))))))
-
- ; ($COND 2 <t1> (LAMBDA () <f1>) $EQ? <var> <int>)
- ; =>
- ; <f1>
- ;
- ; <f1>: ... ($COND 2 <t2> <f2> $EQ? <var> <int2>) ...
- ; =>
- ; ... ($COMPUTED-GOTO 3 <t1> <t2> <f2> <var> '(<int1> <int2>)) ...
- ;
- ; 2 comes before 1 in the GOTO call to preserve the inverse mapping between
- ; the exits of the GOTO and the execution order.
-
- (define (make-goto-call var upper lower)
- (receive (int1 true1 false1)
- (destructure-eq? upper)
- (receive (int2 true2 false2)
- (destructure-eq? lower)
- (let ((primop (get-primop 'computed-goto))
- (keys (list int2 int1)))
- (let-nodes ((c (($ primop) 3 true2 true1 false2 (* var) 'keys)))
- (replace upper (detach (lambda-body false1)))
- (erase-all false1)
- (replace lower c)
- '#t)))))
-
- (define (destructure-eq? call)
- (destructure (((true false #f a1 a2) (call-args call)))
- (return (literal-value (if (literal-node? a1) a1 a2))
- (detach true)
- (detach false))))
-
- ; ($COND 2 <true> (LAMBDA () <false>) $EQ? <var> <int>)
- ; =>
- ; <false>
- ;
- ; f1: ... ($COMPUTED-GOTO N ... <var> '(...)) ...
- ; =>
- ; ... ($COMPUTED-GOTO N+1 <true> ... <var> '(<int> ...)) ...
- ;
- ; If the new value is already in the list, replace the old exit with the
- ; new one.
-
- (define (add-to-goto call eq?-call)
- (let ((exits (call-exits call)))
- (receive (int true false)
- (destructure-eq? eq?-call)
- (replace eq?-call (detach (lambda-body false)))
- (erase-all false)
- (let* ((values-node ((call-arg (fx+ exits '2)) call))
- (values (literal-value values-node)))
- (cond ((memq? int values)
- (do ((vals values (cdr vals))
- (exits (call-args call) (cdr exits)))
- ((fx= int (car vals))
- (replace (car exits) true))))
- (else
- (set (literal-value values-node) (cons int values))
- (set (call-exits call) (fx+ exits '1))
- (let ((args (map detach (call-args call))))
- (modify (cdr (call-proc+args call))
- (lambda (l) (cons empty l)))
- (relate-call-args call (cons true args)))))
- '#t))))
-
- ;;;============================================================================
-
- ;;; Part two, fixup code to turn unnecessary computed gotos back into calls
- ;;; to EQ?
-
- (define computed-goto-minimum-size '5)
- (define computed-goto-miminum-density '0.5)
-
- ; Simplifier version:
- ; ($COMPUTED-GOTO n <cont0> ... <contN-2> <miss> <i> <list of ints>)
- ;
- ; Code generation version:
- ; ($COMPUTED-GOTO n <cont0> ... <contN-2> <i>)
- ;
- ; 1) Break up sparse GOTOs into calls to EQ? and smaller GOTOs
- ; 2) Each remaining GOTO needs range-check calls and base arithmetic
- ;
- ; DATA is a list of (<index> <integer> <action>) lists
-
- (define (fixup-computed-goto call)
- (let* ((exits (call-exits call))
- (fail (detach ((call-arg exits) call)))
- (var (reference-variable ((call-arg (fx+ '1 exits)) call)))
- (ints (literal-value ((call-arg (fx+ '2 exits)) call)))
- (data (do ((i '0 (fx+ i '1))
- (ints ints (cdr ints))
- (args (call-args call) (cdr args))
- (ls '() (cons (list i (car ints) (detach (car args))) ls)))
- ((null? ints)
- (sort-list! ls (lambda (a b) (fx<= (cadr a) (cadr b)))))))
- (min (cadar data))
- (max (cadr (last data)))
- (density (/ (fx- exits '1) (fx+ (fx- max min) '1))))
- (replace call (if (and (fx> exits computed-goto-minimum-size)
- (> density computed-goto-miminum-density))
- (rebuild-computed-goto data min max fail var)
- (computed-goto->eq?s data fail var)))))
-
- ; ($COMPUTED-GOTO n <cont0> ... <contN-2> <miss> <i> <list of ints>)
- ; =>
- ; (LET ((M <miss>)
- ; (I (FX- <i> <low>)))
- ; ($FX< M ^1 I '0))
- ; ^1 = (LAMBDA () ($FX< ^2 M '(- <high> <low>) I))
- ; ^2 = (LAMBDA () ($COMPUTED-GOTO M <contx0> ... <contxM-1> I))
-
- ; PAIRS is a list of (<index> . <lambda-node>) pairs.
-
- (define (rebuild-computed-goto data low high fail-node tested-var)
- (let* ((fail (if (lambda-node? fail-node)
- (create-variable 'f)
- (reference-variable fail-node)))
- (t-var (if (fx= '0 low) tested-var (create-variable 't)))
- (args (create-goto-args data low t-var fail))
- (size (fx+ (fx- high low) '1))
- (call (create-call-node (fx+ size '2) size)))
- (relate call-proc call (create-primop-node (get-primop 'computed-goto)))
- (relate-call-args call args)
- (let ((condp (get-primop 'conditional))
- (testp (get-primop 'fixnum-less?))
- (diff (fx+ (fx- high low) '1))
- (f1 (wrap-in-lambda (create-reference-node fail)))
- (f2 (wrap-in-lambda (create-reference-node fail))))
- (let-nodes ((new (($ condp) 2 f1 l1 ($ testp) (* t-var) '0))
- (l1 () (($ condp) 2 l2 f2 ($ testp) (* t-var) 'diff))
- (l2 () call))
- (let ((new (cond ((lambda-node? fail-node)
- (bind-goto-fail new fail-node fail))
- (else
- (erase-all fail)
- new))))
- (if (fx= low '0)
- new
- (subtract-goto-base new low tested-var t-var)))))))
-
- (define (create-goto-args data low test-var fail-var)
- (let ((test (create-reference-node test-var)))
- (iterate loop ((data data) (i low) (args '()))
- (cond ((null? data)
- (reverse! (cons test (map! wrap-in-lambda args))))
- ((fx= i (cadar data))
- (loop (cdr data)
- (fx+ i '1)
- (cons (caddar data) args)))
- (else
- (loop data
- (fx+ i '1)
- (cons (create-reference-node fail-var) args)))))))
-
- (define (bind-goto-fail call value var)
- (let-nodes ((new (l1 0 value))
- (l1 (#f (var var)) call))
- new))
-
- (define (subtract-goto-base call offset from result)
- (let ((primop (get-primop 'fixnum-subtract)))
- (let-nodes ((new (($ primop) 1 cont (* from) 'offset))
- (cont (#f (x result)) call))
- new)))
-
- ; Turn a GOTO into a series of EQ? tests.
-
- (define (computed-goto->eq?s data fail var)
- (let ((data (sort-list! data (lambda (a b) (fx> (car a) (car b)))))
- (call (detach (lambda-body fail)))
- (cond (get-primop 'conditional))
- (eq (get-primop 'eq?)))
- (erase-all fail)
- (iterate loop ((data data) (call call))
- (if (null? data)
- call
- (destructure ((((#f int exit) . rest) data))
- (let ((exit (wrap-in-lambda exit)))
- (let-nodes ((new (($ cond) 2 exit false ($ eq) (* var) 'int))
- (false () call))
- (loop rest new))))))))
-
- (define (wrap-in-lambda node)
- (if (lambda-node? node)
- node
- (let-nodes ((l1 () (node 0)))
- l1)))
-
- (define (id-primop-ref? node id)
- (and (primop-node? node)
- (eq? id (primop.id (primop-value node)))))
-
- (define (get-primop id)
- (let ((primop (table-entry primop-table id)))
- (if primop primop (bug '"~S primop not found" id))))
-